// Copyright 2022 a.o.smith ALL Rights Reserved
package com.liuxinlong.utils;

import java.security.MessageDigest;
import java.security.SecureRandom;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;

/**
 * 加解密工具类
 * 
 * @author liuxinlong@hotwater.com.cn
 * @version 1.0.0
 * @since 2021-12-30
 */
public class EncryptUtils {

	public static final String MD5 = "MD5";
	public static final String SHA1 = "SHA1";
	public static final String HmacMD5 = "HmacMD5";
	public static final String HmacSHA1 = "HmacSHA1";
	public static final String DES = "DES";
	public static final String AES = "AES";

	/** 编码格式；默认使用uft-8 */
	public static String charset = "utf-8";
	/** DES */
	public static int keysizeDES = 0;
	/** AES */
	public static int keysizeAES = 128;

	/** AESkey */
	public static String AES_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCpuML1SitErm/b7dXvsTQ3wWM4";

	public static EncryptUtils me;

	private EncryptUtils() {
		// 单例
	}

	// 双重锁
	public static EncryptUtils getInstance() {
		if (me == null) {
			synchronized (EncryptUtils.class) {
				if (me == null) {
					me = new EncryptUtils();
				}
			}
		}
		return me;
	}

	/**
	 * 使用MessageDigest进行单向加密（无密码）
	 * 
	 * @param res       被加密的文本
	 * @param algorithm 加密算法名称
	 * @return
	 */
	private String messageDigest(String res, String algorithm) {
		try {
			MessageDigest md = MessageDigest.getInstance(algorithm);
			byte[] resBytes = charset == null ? res.getBytes() : res.getBytes(charset);
			return base64(md.digest(resBytes));
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 使用KeyGenerator进行单向/双向加密（可设密码）
	 * 
	 * @param res       被加密的原文
	 * @param algorithm 加密使用的算法名称
	 * @param key       加密使用的秘钥
	 * @return
	 */
	private String keyGeneratorMac(String res, String algorithm, String key) {
		try {
			SecretKey sk = null;
			if (key == null) {
				KeyGenerator kg = KeyGenerator.getInstance(algorithm);
				sk = kg.generateKey();
			} else {
				byte[] keyBytes = charset == null ? key.getBytes() : key.getBytes(charset);
				sk = new SecretKeySpec(keyBytes, algorithm);
			}
			Mac mac = Mac.getInstance(algorithm);
			mac.init(sk);
			byte[] result = mac.doFinal(res.getBytes());
			return base64(result);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 使用KeyGenerator双向加密，DES/AES，注意这里转化为字符串的时候是将2进制转为16进制格式的字符串，不是直接转，因为会出错
	 * 
	 * @param res       加密的原文
	 * @param algorithm 加密使用的算法名称
	 * @param key       加密的秘钥
	 * @param keysize
	 * @param isEncode
	 * @return
	 */
	private static String keyGeneratorES(String res, String algorithm, String key, int keysize, boolean isEncode) {
		try {
			KeyGenerator kg = KeyGenerator.getInstance(algorithm);
			SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
			if (keysize == 0) {
				byte[] keyBytes = charset == null ? key.getBytes() : key.getBytes(charset);
				secureRandom.setSeed(keyBytes);
				kg.init(128,secureRandom);
			} else if (key == null) {
				kg.init(keysize);
			} else {
				byte[] keyBytes = charset == null ? key.getBytes() : key.getBytes(charset);
				secureRandom.setSeed(keyBytes);
				kg.init(keysize, secureRandom);
			}
			SecretKey sk = kg.generateKey();
			SecretKeySpec sks = new SecretKeySpec(sk.getEncoded(), algorithm);
			Cipher cipher = Cipher.getInstance(algorithm);
			if (isEncode) {
				cipher.init(Cipher.ENCRYPT_MODE, sks);
				byte[] resBytes = charset == null ? res.getBytes() : res.getBytes(charset);
				return parseByte2HexStr(cipher.doFinal(resBytes));
			} else {
				cipher.init(Cipher.DECRYPT_MODE, sks);
				return new String(cipher.doFinal(parseHexStr2Byte(res)));
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	private String base64(byte[] res) {
		return Base64.encode(res);
	}

	/** 将二进制转换成16进制 */
	public static String parseByte2HexStr(byte buf[]) {
		StringBuffer sb = new StringBuffer();
		for (int i = 0; i < buf.length; i++) {
			String hex = Integer.toHexString(buf[i] & 0xFF);
			if (hex.length() == 1) {
				hex = '0' + hex;
			}
			sb.append(hex.toUpperCase());
		}
		return sb.toString();
	}

	/** 将16进制转换为二进制 */
	public static byte[] parseHexStr2Byte(String hexStr) {
		if (hexStr.length() < 1)
			return null;
		byte[] result = new byte[hexStr.length() / 2];
		for (int i = 0; i < hexStr.length() / 2; i++) {
			int high = Integer.parseInt(hexStr.substring(i * 2, i * 2 + 1), 16);
			int low = Integer.parseInt(hexStr.substring(i * 2 + 1, i * 2 + 2), 16);
			result[i] = (byte) (high * 16 + low);
		}
		return result;
	}

	/**
	 * md5加密算法进行加密（不可逆）
	 * 
	 * @param res 需要加密的原文
	 * @return
	 */
	public String MD5(String res) {
		return messageDigest(res, MD5);
	}

	/**
	 * md5加密算法进行加密（不可逆）
	 * 
	 * @param res 需要加密的原文
	 * @param key 秘钥
	 * @return
	 */
	public String MD5(String res, String key) {
		return keyGeneratorMac(res, HmacMD5, key);
	}

	/**
	 * 使用SHA1加密算法进行加密（不可逆）
	 * 
	 * @param res 需要加密的原文
	 * @return
	 */
	public String SHA1(String res) {
		return messageDigest(res, SHA1);
	}

	/**
	 * 使用SHA1加密算法进行加密（不可逆）
	 * 
	 * @param res 需要加密的原文
	 * @param key 秘钥
	 * @return
	 */
	public String SHA1(String res, String key) {
		return keyGeneratorMac(res, HmacSHA1, key);
	}

	/**
	 * 使用DES加密算法进行加密（可逆）
	 * 
	 * @param res 需要加密的原文
	 * @param key 秘钥
	 * @return
	 */
	public String DESencode(String res, String key) {
		return keyGeneratorES(res, DES, key, keysizeDES, true);
	}

	/**
	 * 对使用DES加密算法的密文进行解密（可逆）
	 * 
	 * @param res 需要解密的密文
	 * @param key 秘钥
	 * @return
	 */
	public String DESdecode(String res, String key) {
		return keyGeneratorES(res, DES, key, keysizeDES, false);
	}

	/**
	 * 使用AES加密算法经行加密（可逆）
	 * 
	 * @param res 需要加密的密文
	 * @param key 秘钥
	 * @return
	 */
	public static String AESencode(String res, String key) {
		return keyGeneratorES(res, AES, key, keysizeAES, true);
	}

	/**
	 * 对使用AES加密算法的密文进行解密
	 * 
	 * @param res 需要解密的密文
	 * @param key 秘钥
	 * @return
	 */
	public static String AESdecode(String res, String key) {
		return keyGeneratorES(res, AES, key, keysizeAES, false);
	}

	/**
	 * 使用AES加密算法经行加密（可逆）
	 *
	 * @param res 需要加密的明文
	 * @return 加密后的密文
	 */
	public static String AESEncode(String res) {
		return AESencode(res,AES_KEY);
	}

	/**
	 * 对使用AES加密算法的密文进行解密
	 *
	 * @param res 需要解密的密文
	 * @return 解密后的明文
	 */
	public static String AESDecode(String res) {
		return AESdecode(res,AES_KEY);
	}
	/**
	 * 使用异或进行加密
	 * 
	 * @param res 需要加密的密文
	 * @param key 秘钥
	 * @return
	 */
	public String XORencode(String res, String key) {
		byte[] bs = res.getBytes();
		for (int i = 0; i < bs.length; i++) {
			bs[i] = (byte) ((bs[i]) ^ key.hashCode());
		}
		return parseByte2HexStr(bs);
	}

	/**
	 * 使用异或进行解密
	 * 
	 * @param res 需要解密的密文
	 * @param key 秘钥
	 * @return
	 */
	public String XORdecode(String res, String key) {
		byte[] bs = parseHexStr2Byte(res);
		for (int i = 0; i < bs.length; i++) {
			bs[i] = (byte) ((bs[i]) ^ key.hashCode());
		}
		return new String(bs);
	}

	/**
	 * 直接使用异或（第一调用加密，第二次调用解密）
	 * 
	 * @param res 密文
	 * @param key 秘钥
	 * @return
	 */
	public int XOR(int res, String key) {
		return res ^ key.hashCode();
	}

	/**
	 * 使用Base64进行加密
	 * 
	 * @param res 密文
	 * @return
	 */
	public static String Base64Encode(String res) {
		return Base64.encode(res.getBytes());
	}

	/**
	 * 使用Base64进行解密
	 * 
	 * @param res
	 * @return
	 */
	public static String Base64Decode(String res) {
		return new String(Base64.decode(res));
	}

	/**
	 * 将加密后的字节数组转换成字符串
	 *
	 * @param b 字节数组
	 * @return 字符串
	 */
	public static String byteArrayToHexString(byte[] b) {
		StringBuilder hs = new StringBuilder();
		String stmp;
		for (int n = 0; b != null && n < b.length; n++) {
			stmp = Integer.toHexString(b[n] & 0XFF);
			if (stmp.length() == 1)
				hs.append('0');
			hs.append(stmp);
		}
		return hs.toString().toLowerCase();
	}

	/**
	 * sha256_HMAC加密
	 * 
	 * @param message 消息
	 * @param secret  秘钥
	 * @return 加密后字符串
	 */
	public static String sha256_HMAC_Encrypt(String message, String secret) {
		String hash = "";
		try {
			Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
			SecretKeySpec secret_key = new SecretKeySpec(secret.getBytes(), "HmacSHA256");
			sha256_HMAC.init(secret_key);
			byte[] bytes = sha256_HMAC.doFinal(message.getBytes());
			hash = byteArrayToHexString(bytes);
		} catch (Exception e) {

		}
		return hash;
	}

}
